package com.yuan.yblog.security.config;

import com.yuan.yblog.security.component.*;
import com.yuan.yblog.security.handler.LogoutSuccessHandlerImpl;
import com.yuan.yblog.security.handler.RestAuthenticationEntryPoint;
import com.yuan.yblog.security.handler.RestfulAccessDeniedHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.List;

/**
 * SpringSecurity配置类
 */

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    IgnoreUrlsConfig ignoreUrlsConfig;
    @Autowired
    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    @Autowired
    UserDetailsService userDetailsService;
    @Autowired
    CustomPasswordEncoder customPasswordEncoder;
    @Autowired
    LogoutSuccessHandlerImpl logoutSuccessHandler;


    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                /* 拦截器器，主要用于校验jwt，位置：放在用户密码验证过滤器之前 */
                .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)

                /*关闭跨站请求防护及*/
                .csrf(AbstractHttpConfigurer::disable)

                /*不使用session*/
                .sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

                /* 自定义4xx处理类 */
                .exceptionHandling(handle->{
                    // 认证失败处理
                    handle.authenticationEntryPoint(restAuthenticationEntryPoint);
                    // 鉴权失败处理
                    handle.accessDeniedHandler(restfulAccessDeniedHandler);
                })

                /* 配置请求路径放行规则 */
                .authorizeHttpRequests(auth -> {
                    auth
                            /* 放行预检OPTIONS请求，允许跨域请求的OPTIONS请求 */
                            .requestMatchers(HttpMethod.OPTIONS).permitAll()
                            /* 指定匿名路径 */
                            .requestMatchers(getWhitelistArray()).permitAll()
                            /* 其余路径均要认证 */
                            .anyRequest().authenticated();
                })

                /* 自定义登出处理 */
                .logout(logout->{
                    logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
                })
        ;

        return http.build();
    }

    /**
     * 读取yml配置的白名单url
     * @return 白名单数组
     */
    public String[] getWhitelistArray() {
        List<String> urls = ignoreUrlsConfig.getUrls();
        String[] anonymousPath = urls.toArray(new String[0]);
        return anonymousPath;
    }


    /**
     * 用户凭证管理器：用于密码校验、获取当前用户凭证
     */
    @Bean
    public AuthenticationManager authenticationManager() throws Exception{
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        // 确定数据库的查询类
        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        // 确定密码的加密方式
        daoAuthenticationProvider.setPasswordEncoder(customPasswordEncoder);
        ProviderManager pm = new ProviderManager(daoAuthenticationProvider);
        return pm;
    }



}
